﻿using System;
using System.Collections.Generic;
using System.Text;
using Stauffware.Common.Core;
using Stauffware.Common.Console;
using NUnit.Framework;
using NU=NUnit.Framework;

namespace UnitTests.CommandLineParser
{
	//#################################################################################
	/// <summary>
	/// Unit tests for command-line parameters backed by fields (instance variables).
	/// </summary>

	public class Fields_UnitTests
	{
		[TestCase("string1", "hello")]
		[TestCase("integer1", "0")]
		[TestCase("float1", "3.14159")]
		[TestCase("boolean2", "true")]
		[TestCase("boolean2", "false")]
		[TestCase("enum1", "value2")]
		[TestCase("flags1", "flagA")]
		[TestCase("flags1", "flagA,flagC")]
		[TestCase("arrayOstrings1", "this", "is", "a", "test")]
		[TestCase("listOstrings1", "pronoun", "trouble")]
		[TestCase("string1", "~!@#$%^&*()_-+=|\\{}[]:;\"\'<,>.?/`")]
		public void GoodArgs (params string[] inputs)
		{
			ClassWithFields testData = new ClassWithFields();
			CmdLineParser clp = new CmdLineParser(testData);
			clp.ParseCmdLine(inputs);
			switch (inputs[0])
			{
				case "string1": NU.Assert.AreEqual(testData.String1, inputs[1]); break;
				case "integer1": NU.Assert.AreEqual(testData.Integer1, int.Parse(inputs[1])); break;
				case "float1": /* NU.Assert.AreEqual(testData.Float1, float.Parse(inputs[1])); */ break;
				case "enum1": NU.Assert.AreEqual(testData.Enum1, 
								Enum.Parse(typeof(ClassWithFields.MyEnum), inputs[1], true)); break;
				case "flags1": NU.Assert.AreEqual(testData.Flags1,
								Enum.Parse(typeof(ClassWithFields.MyFlags), inputs[1], true)); break;
				case "boolean2": NU.Assert.AreEqual(testData.Boolean2, Boolean.Parse(inputs[1])); break;
				case "arrayOstrings1":
				{
					string[] actual = new string[inputs.Length-1];
					Array.Copy(inputs, 1, actual, 0, actual.Length);
					NU.Assert.AreEqual(testData.ArrayOStrings1, actual);
					break;
				}
				case "listOstrings1":
				{
					List<string> actual = new List<string>();
					for (int i = 1; i < inputs.Length; i++)
						actual.Add(inputs[i]);
					NU.Assert.AreEqual(testData.ListOStrings1, actual);
					break;
				}
				default: Stauffware.Common.Core.Assert.NoCaseFor(inputs[0]); break;
			}
		}

		[TestCase("boolean1", true)]
		[TestCase(null, false)]
		public void NiladicBoolean (string input, bool expectedValue)
		{
			List<string> inputs = new List<string>();
			if (input != null)
				inputs.Add(input);

			ClassWithFields testData = new ClassWithFields();
			CmdLineParser clp = new CmdLineParser(testData);
			clp.ParseCmdLine(inputs);

			NU.Assert.AreEqual(expectedValue, testData.Boolean1);
		}

		//[TestCase("boolean2", "true", true)]
		//[TestCase("boolean2", "false", false)]
		//[TestCase(null, null, null)]
		//public void NullableBoolean (string argName, string argValue, bool? expectedValue)
		//{
		//    List<string> inputs = new List<string>();
		//    if (input != null)
		//        inputs.Add(input);

		//    ClassWithFields testData = new ClassWithFields();
		//    CLP.CmdLineParser clp = new CLP.CmdLineParser(testData);
		//    clp.ParseCmdLine(inputs);

		//    NU.Assert.AreEqual(expectedValue, testData.Boolean2);
		//}
	}

	//=================================================================================
	/// <summary>
	/// Test data for Fields_UnitTests.
	/// </summary>

	public class ClassWithFields
	{
		public enum MyEnum { Value1, Value2, Value3 };

		[Flags]
		public enum MyFlags { FlagA=1, FlagB=2, FlagC=4, FlagD=8 };

		public string String1;

		public byte Byte1;
		public int Integer1;
		public double Float1;
		public bool Boolean1;
		public MyEnum Enum1 = MyEnum.Value2;
		public MyFlags Flags1 = MyFlags.FlagA | MyFlags.FlagD;

		public byte? Byte2;
		public int? Integer2;
		public double? Float2;
		public bool? Boolean2;
		public MyEnum? Enum2;
		public MyFlags? Flags2;

		public List<string> ListOStrings1;
		public List<string> ListOStrings2 = new List<string>();

		public string[] ArrayOStrings1;
		public string[] ArrayOStrings2 = new string[5] { "em", "oh", "you", "ess", "ee" };

		public ClassWithFields ( )
		{
			this.ListOStrings2.Add("angel");
			this.ListOStrings2.Add("devil");
		}
	}

	//#################################################################################
	/// <summary>
	/// Unit tests for command-line parameters backed by properties.
	/// </summary>

	public class Properties_UnitTests
	{
		[TestCase("string1", "hello")]
		[TestCase("integer1", "0")]
		[TestCase("float1", "3.14159")]
		[TestCase("boolean2", "false")]
		[TestCase("boolean2", "true")]
		[TestCase("arrayOstrings1", "this", "is", "a", "test")]
		[TestCase("listOstrings1", "pronoun", "trouble")]
		public void GoodArgs (params string[] inputs)
		{
			ClassWithProperties testData = new ClassWithProperties();
			CmdLineParser clp = new CmdLineParser(testData);
			clp.ParseCmdLine(inputs);
			switch (inputs[0])
			{
				case "string1": NU.Assert.AreEqual(testData.String1, inputs[1]); break;
				case "integer1": NU.Assert.AreEqual(testData.Integer1, int.Parse(inputs[1])); break;
				case "float1": /* NU.Assert.AreEqual(testData.Float1, float.Parse(inputs[1])); */ break;
				case "boolean2": NU.Assert.AreEqual(testData.Boolean2, Boolean.Parse(inputs[1])); break;
				case "arrayOstrings1":
					{
						string[] actual = new string[inputs.Length-1];
						Array.Copy(inputs, 1, actual, 0, actual.Length);
						NU.Assert.AreEqual(testData.ArrayOStrings1, actual);
						break;
					}
				case "listOstrings1":
					{
						List<string> actual = new List<string>();
						for (int i = 1; i < inputs.Length; i++)
							actual.Add(inputs[i]);
						NU.Assert.AreEqual(testData.ListOStrings1, actual);
						break;
					}
				default: Stauffware.Common.Core.Assert.NoCaseFor(inputs[0]); break;
			}
		}

		[TestCase("boolean1", true)]
		[TestCase(null, false)]
		public void NiladicBoolean (string input, bool expectedValue)
		{
			List<string> inputs = new List<string>();
			if (input != null)
				inputs.Add(input);

			ClassWithProperties testData = new ClassWithProperties();
			CmdLineParser clp = new CmdLineParser(testData);
			clp.ParseCmdLine(inputs);

			NU.Assert.AreEqual(expectedValue, testData.Boolean1);
		}

		//[TestCase("boolean2=true", true)]
		//[TestCase("boolean2=false", false)]
		//[TestCase(null, null)]
		//public void NullableBoolean (string input, bool? expectedValue)
		//{
		//    List<string> inputs = new List<string>();
		//    if (input != null)
		//        inputs.Add(input);

		//    ClassWithProperties testData = new ClassWithProperties();
		//    CLP.CmdLineParser clp = new CLP.CmdLineParser(testData);
		//    clp.ParseCmdLine(inputs);

		//    NU.Assert.AreEqual(expectedValue, testData.Boolean2);
		//}

	//	[TestCase(false, "", "")]
		[TestCase(false, "string1", "Hello, CmdLineParser!", "boolean2", "true")]
		[TestCase(true, "string1", "Hello, CmdLineParser!", "boolean2", "true")]
		public void DumpParams (bool all, params string[] inputs)
		{
			ClassWithProperties testData = new ClassWithProperties();
			CmdLineParser clp = new CmdLineParser(testData);
			clp.ParseCmdLine(inputs);
			System.Console.Out.WriteLine("Command Line Parameters:");
			clp.LogParams(System.Console.Out, all, "\t");
		}
	}

	//=================================================================================
	/// <summary>
	/// Test data for Properties_UnitTests.
	/// </summary>

	public class ClassWithProperties
	{
		public string String1 { get; set; }

		public byte Byte1 { get; set; }
		public int Integer1 { get; set; }
		public double Float1 { get; set; }
		public bool Boolean1 { get; set; }

		public byte? Byte2 { get; set; }
		public int? Integer2 { get; set; }
		public double? Float2 { get; set; }
		public bool? Boolean2 { get; set; }

		public List<string> ListOStrings1 { get; set; }
		public List<string> ListOStrings2 { get; set; }

		public string[] ArrayOStrings1 { get; set; }
		public string[] ArrayOStrings2 = new string[5];

		public ClassWithProperties ( )
		{
			this.ListOStrings2 = new List<string>();
			this.ListOStrings2.Add("angel");
			this.ListOStrings2.Add("devil");
			this.Float1 = Math.PI;
			this.Integer2 = 17;

			this.ArrayOStrings2 = new string[5] { "em", "oh", "you", "ess", "ee" };
		}
	}

	//=================================================================================

	public class OtherUnitTests
	{
		[Test()]
		public static void Help ( )
		{
			System.Console.WriteLine("Help!");

			if (!ConsoleUtils.ConsoleExists())
				if (!ConsoleUtils.TryAttachToParentConsole(true))
					System.Console.WriteLine("can't get a console");
				//	NU.Assert.Fail("can't get a console");

			ClassWithProperties data = new ClassWithProperties();
			CmdLineParser clp = new CmdLineParser(data);
			clp.HelpExitCode = null;	// prevent Help param from exiting program
			List<string> args = new List<string>();
			args.Add("-?");
			clp.ParseCmdLine(args);
		}
	}

	//#################################################################################
}
